package io.modelcontextprotocol.autoconfigure.utils;


import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.OptionPreset;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import com.github.victools.jsonschema.generator.SchemaVersion;
import com.github.victools.jsonschema.module.jackson.JacksonModule;
import com.github.victools.jsonschema.module.jackson.JacksonOption;
import com.github.victools.jsonschema.module.swagger2.Swagger2Module;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import io.modelcontextprotocol.util.ListUtils;
import io.modelcontextprotocol.util.MapUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.KotlinDetector;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

public abstract class ModelOptionsUtils {
    public static final ObjectMapper OBJECT_MAPPER;
    private static final List<String> BEAN_MERGE_FIELD_EXCISIONS;
    private static final ConcurrentHashMap<Class<?>, List<String>> REQUEST_FIELD_NAMES_PER_CLASS;
    private static final AtomicReference<SchemaGenerator> SCHEMA_GENERATOR_CACHE;
    private static TypeReference<HashMap<String, Object>> MAP_TYPE_REF;

    public ModelOptionsUtils() {
    }

    public static Map<String, Object> jsonToMap(String json) {
        return jsonToMap(json, OBJECT_MAPPER);
    }

    public static Map<String, Object> jsonToMap(String json, ObjectMapper objectMapper) {
        try {
            return (Map)objectMapper.readValue(json, MAP_TYPE_REF);
        } catch (Exception var3) {
            Exception e = var3;
            throw new RuntimeException(e);
        }
    }

    public static <T> T jsonToObject(String json, Class<T> type) {
        try {
            return OBJECT_MAPPER.readValue(json, type);
        } catch (Exception var3) {
            Exception e = var3;
            throw new RuntimeException("Failed to json: " + json, e);
        }
    }

    public static String toJsonString(Object object) {
        try {
            return OBJECT_MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException var2) {
            JsonProcessingException e = var2;
            throw new RuntimeException(e);
        }
    }

    public static String toJsonStringPrettyPrinter(Object object) {
        try {
            return OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
        } catch (JsonProcessingException var2) {
            JsonProcessingException e = var2;
            throw new RuntimeException(e);
        }
    }

    public static <T> T merge(Object source, Object target, Class<T> clazz, List<String> acceptedFieldNames) {
        if (source == null) {
            source = MapUtils.of();
        }

        List<String> requestFieldNames = CollectionUtils.isEmpty(acceptedFieldNames) ? (List)REQUEST_FIELD_NAMES_PER_CLASS.computeIfAbsent(clazz, ModelOptionsUtils::getJsonPropertyValues) : acceptedFieldNames;
        if (CollectionUtils.isEmpty(requestFieldNames)) {
            throw new IllegalArgumentException("No @JsonProperty fields found in the " + clazz.getName());
        } else {
            Map<String, Object> sourceMap = objectToMap(source);
            Map<String, Object> targetMap = objectToMap(target);
            targetMap.putAll((Map)sourceMap.entrySet().stream().filter((e) -> {
                return e.getValue() != null;
            }).collect(Collectors.toMap((e) -> {
                return (String)e.getKey();
            }, (e) -> {
                return e.getValue();
            })));
            targetMap = (Map)targetMap.entrySet().stream().filter((e) -> {
                return requestFieldNames.contains(e.getKey());
            }).collect(Collectors.toMap((e) -> {
                return (String)e.getKey();
            }, (e) -> {
                return e.getValue();
            }));
            return mapToClass(targetMap, clazz);
        }
    }

    public static <T> T merge(Object source, Object target, Class<T> clazz) {
        return (T)merge(source, target, clazz, (List)null);
    }

    public static Map<String, Object> objectToMap(Object source) {
        if (source == null) {
            return new HashMap();
        } else {
            try {
                String json = OBJECT_MAPPER.writeValueAsString(source);
                return (Map)((Map<String, Object>)OBJECT_MAPPER.readValue(json, new TypeReference<Map<String, Object>>() {
                })).entrySet().stream().filter((ex) -> {
                    return ex.getValue() != null;
                }).collect(Collectors.toMap((ex) -> {
                    return (String)ex.getKey();
                }, (ex) -> {
                    return ex.getValue();
                }));
            } catch (JsonProcessingException var2) {
                JsonProcessingException e = var2;
                throw new RuntimeException(e);
            }
        }
    }

    public static <T> T mapToClass(Map<String, Object> source, Class<T> clazz) {
        try {
            String json = OBJECT_MAPPER.writeValueAsString(source);
            return OBJECT_MAPPER.readValue(json, clazz);
        } catch (JsonProcessingException var3) {
            JsonProcessingException e = var3;
            throw new RuntimeException(e);
        }
    }

    public static List<String> getJsonPropertyValues(Class<?> clazz) {
        List<String> values = new ArrayList();
        Field[] fields = clazz.getDeclaredFields();
        Field[] var3 = fields;
        int var4 = fields.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Field field = var3[var5];
            JsonProperty jsonPropertyAnnotation = (JsonProperty)field.getAnnotation(JsonProperty.class);
            if (jsonPropertyAnnotation != null) {
                values.add(jsonPropertyAnnotation.value());
            }
        }

        return values;
    }

    public static <I, S extends I, T extends S> T copyToTarget(S sourceBean, Class<I> sourceInterfaceClazz, Class<T> targetBeanClazz) {
        Assert.notNull(sourceInterfaceClazz, "SourceOptionsClazz must not be null");
        Assert.notNull(targetBeanClazz, "TargetOptionsClazz must not be null");
        if (sourceBean == null) {
            return null;
        } else if (sourceBean.getClass().isAssignableFrom(targetBeanClazz)) {
            return (T)sourceBean;
        } else {
            try {
                T targetOptions = targetBeanClazz.getConstructor().newInstance();
                mergeBeans(sourceBean, targetOptions, sourceInterfaceClazz, true);
                return targetOptions;
            } catch (Exception var4) {
                Exception e = var4;
                throw new RuntimeException("Failed to convert the " + sourceInterfaceClazz.getName() + " into " + targetBeanClazz.getName(), e);
            }
        }
    }

    public static <I, S extends I, T extends S> T mergeBeans(S source, T target, Class<I> sourceInterfaceClazz, boolean overrideNonNullTargetValues) {
        Assert.notNull(source, "Source object must not be null");
        Assert.notNull(target, "Target object must not be null");
        BeanWrapper sourceBeanWrap = new BeanWrapperImpl(source);
        BeanWrapper targetBeanWrap = new BeanWrapperImpl(target);
        List<String> interfaceNames = Arrays.stream(sourceInterfaceClazz.getMethods()).map((m) -> {
            return m.getName();
        }).collect(Collectors.toList());
        PropertyDescriptor[] var7 = sourceBeanWrap.getPropertyDescriptors();
        int var8 = var7.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor descriptor = var7[var9];
            if (!BEAN_MERGE_FIELD_EXCISIONS.contains(descriptor.getName()) && interfaceNames.contains(toGetName(descriptor.getName()))) {
                String propertyName = descriptor.getName();
                Object value = sourceBeanWrap.getPropertyValue(propertyName);
                if (value != null) {
                    Object targetValue = targetBeanWrap.getPropertyValue(propertyName);
                    if (targetValue == null || overrideNonNullTargetValues) {
                        targetBeanWrap.setPropertyValue(propertyName, value);
                    }
                }
            }
        }

        return target;
    }

    private static String toGetName(String name) {
        String var10000 = name.substring(0, 1).toUpperCase();
        return "get" + var10000 + name.substring(1);
    }

    public static String getJsonSchema(Type inputType, boolean toUpperCaseTypeValues) {
        if (SCHEMA_GENERATOR_CACHE.get() == null) {
            JacksonModule jacksonModule = new JacksonModule(new JacksonOption[]{JacksonOption.RESPECT_JSONPROPERTY_REQUIRED});
            Swagger2Module swaggerModule = new Swagger2Module();
            SchemaGeneratorConfigBuilder configBuilder = (new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON)).with(Option.EXTRA_OPEN_API_FORMAT_VALUES, new Option[0]).with(Option.PLAIN_DEFINITION_KEYS, new Option[0]).with(swaggerModule).with(jacksonModule);


            SchemaGeneratorConfig config = configBuilder.build();
            SchemaGenerator generator = new SchemaGenerator(config);
            SCHEMA_GENERATOR_CACHE.compareAndSet((SchemaGenerator)null, generator);
        }

        ObjectNode node = ((SchemaGenerator)SCHEMA_GENERATOR_CACHE.get()).generateSchema(inputType, new Type[0]);
        if (inputType == Void.class && !node.has("properties")) {
            node.putObject("properties");
        }

        if (toUpperCaseTypeValues) {
            toUpperCaseTypeValues(node);
        }

        return node.toPrettyString();
    }

    public static void toUpperCaseTypeValues(ObjectNode node) {
        if (node != null) {
            if (node.isObject()) {
                node.fields().forEachRemaining((entry) -> {
                    JsonNode value = (JsonNode)entry.getValue();
                    if (value.isObject()) {
                        toUpperCaseTypeValues((ObjectNode)value);
                    } else if (value.isArray()) {
                        ((ArrayNode)value).elements().forEachRemaining((element) -> {
                            if (element.isObject() || element.isArray()) {
                                toUpperCaseTypeValues((ObjectNode)element);
                            }

                        });
                    } else if (value.isTextual() && ((String)entry.getKey()).equals("type")) {
                        String oldValue = node.get("type").asText();
                        node.put("type", oldValue.toUpperCase());
                    }

                });
            } else if (node.isArray()) {
                node.elements().forEachRemaining((element) -> {
                    if (element.isObject() || element.isArray()) {
                        toUpperCaseTypeValues((ObjectNode)element);
                    }

                });
            }

        }
    }

    public static <T> T mergeOption(T runtimeValue, T defaultValue) {
        return ObjectUtils.isEmpty(runtimeValue) ? defaultValue : runtimeValue;
    }

    static {
        OBJECT_MAPPER = ((JsonMapper)((JsonMapper.Builder)((JsonMapper.Builder)((JsonMapper.Builder)JsonMapper.builder().disable(new DeserializationFeature[]{DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES})).disable(new SerializationFeature[]{SerializationFeature.FAIL_ON_EMPTY_BEANS})).addModules(JacksonUtils.instantiateAvailableModules())).build()).configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        BEAN_MERGE_FIELD_EXCISIONS = ListUtils.of("class");
        REQUEST_FIELD_NAMES_PER_CLASS = new ConcurrentHashMap();
        SCHEMA_GENERATOR_CACHE = new AtomicReference();
        MAP_TYPE_REF = new TypeReference<HashMap<String, Object>>() {
        };
    }
}
